<?php

declare(strict_types=1);

namespace app\common\service;

use app\common\ImageHandle;
use setasign\Fpdi\Tcpdf\Fpdi;
use think\facade\Log;
use think\File;
use app\common\AliyunOss\Image as AliyunOssImage;
use app\common\model\Images;

class Upload
{

    protected $imageHandler;
    protected $aliyun_oss;
    protected $image;
    protected $oss_domain_name;

    public function __construct()
    {
        $this->imageHandler = new ImageHandle();
        $this->aliyun_oss = new AliyunOssImage();
        $this->oss_domain_name = 'https://' . config('filesystem.aliyun-oss.bucket') . '.' . config('filesystem.aliyun-oss.endpoint');
        $this->image = [
            'limit' => [
                'type_limit' => explode(',', config('filesystem.file.image.type_limit')),
                'max_size_limit' => config('filesystem.file.image.max_size_limit')
            ],
            'temp_path' => root_path() . 'public/static/uploads/temp/'
        ];
    }
    /**
     * @describe base64 上传图片
     * @author: Bobo
     * @date: 2024-06-28 11:38:15
     * @param [type] @required
     * @return Renderable
     */
    public function uploads(string $base64String, int $directory, $is_mark = 0)
    {
        // 找到逗号分隔符，它之前的部分包括了数据类型和编码信息
        $commaPosition = strpos($base64String, ',');
        // 获取逗号之前的部分，即包含数据类型的部分
        $dataTypeSection = substr($base64String, 0, $commaPosition);

        // 使用正则表达式匹配 MIME 类型
        if (preg_match('/^data:\s*([^;]+);base64/i', $dataTypeSection, $matches)) {
            $mimeType = $matches[1];


            switch ($mimeType) {
                case 'image/jpeg': // 处理 JPEG 图像
                case 'image/png':  // 处理 PNG 图像
                    $result = $this->imageHandler->uploadImgByBase64($base64String, $directory);
                    break;
                case 'application/pdf':
                    $result = $this->imageHandler->uploadImgByBase64($base64String, $directory);

                    break;
                case 'application/msword':
                    // 处理 Word 文档（旧版 .doc）
                    break;
                case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
                    // 处理 Word 文档（.docx）
                    break;
                case 'application/vnd.ms-excel':
                    // 处理 Excel 文档（旧版 .xls）
                    break;
                case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
                    // 处理 Excel 文档（.xlsx）
                    break;
                case 'text/plain':
                    // 处理纯文本文件
                    break;
                // 更多的 MIME 类型可以继续添加...
                default:
                    $result = [
                        'url' => '',
                        'result' => false,
                        'message' => '上传失败，不支持的文件类型'
                    ];
                    break;
            }
            if ($result['result']) {
                return ['code' => 0,  'data' => [
                    'id' => $result['id'],
                    'url' => $result['url']
                ]];
            } else {
                return ['code' => 100040, 'message' => $result['message']];
            }
        } else {
            return ['code' => 100043, 'message' => '请检查上传文件类型'];
        }
    }

    /**
     * @describe 批量上传文件
     * @author: Bobo
     * @date: 2025-07-21 13:14:16 
     * @param array $files 文件数组
     * @param int $directory 目录类型
     * @param int $is_mark 是否添加水印
     * @return array 上传结果
     */
    public function batchUploadFile($files, $directory = 0, $is_mark = 0)
    {
        $res = [];
        $successCount = 0;
        $failCount = 0;

        foreach ($files as $file) {
            $fileType =  $file->getMime();

            switch ($fileType) {
                case 'image/jpeg':
                case 'image/png':
                case 'image/gif':
                case 'image/webp':
                    $result = $this->uploadImages($file, $directory, 0, 0, $is_mark);
                    break;
                case 'application/pdf':
                    $result = $this->uploadFile($file, $directory, $is_mark);
                    break;
                case 'application/msword':
                    $result = $this->uploadFile($file, $directory, $is_mark);
                    break;
                case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
                    $result = $this->uploadFile($file, $directory, $is_mark);
                    break;
                case 'application/vnd.ms-excel':
                    $result = $this->uploadFile($file, $directory, $is_mark);
                    break;
                case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
                    $result = $this->uploadFile($file, $directory, $is_mark);
                    break;
                case 'application/vnd.ms-powerpoint':
                    $result = $this->uploadFile($file, $directory, $is_mark);
                    break;
                case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
                    $result = $this->uploadFile($file, $directory, $is_mark);
                    break;
                default:
                    $result = $this->uploadFile($file, $directory);
                    break;
            }
            if (isset($result['code']) && $result['code'] === 0) {
                $successCount++;
                $res[] = $result['data']; // 只保存文件信息
            } else {
                $failCount++;
            }
        }

        return [
            'code' => $failCount === 0 ? 0 : 100044,
            'message' => "批量上传完成，成功：{$successCount}个，失败：{$failCount}个",
            'data' => $res
        ];
    }
    /**
     * @describe file 文件流上传文件
     * @author: Bobo
     * @date: 2024-08-26 19:16:37
     * @param [type] @required
     * @return Renderable
     */
    public function uploadFile($fileObj, int $directory, $is_mark = 0)
    {
        $return = [
            'code' => 0,
            'message' => '',
            'data' => []
        ];

        try {
            $fileExtension = $fileObj instanceof \think\file\UploadedFile ?
                $fileObj->getOriginalExtension() :
                pathinfo($fileObj->getPathname(), PATHINFO_EXTENSION);
            $originalName = $fileObj instanceof \think\file\UploadedFile ?
                $fileObj->getOriginalName() :
                basename($fileObj->getPathname());


            $processedFile = null;

            // === PDF加水印处理 ===
            if ($is_mark) {
                // 配置水印图片路径
                $watermarkPath =  public_path() . 'static/images/watermark.png';

                if (!file_exists($watermarkPath)) {
                    throw new \Exception("水印图片不存在: {$watermarkPath}");
                }

                // 创建加水印的临时文件
                $watermarkedPdf = tempnam(sys_get_temp_dir(), 'wm_') . '.pdf';

                // 调用PDF加水印方法
                $watermarkSuccess = $this->addPdfWatermark(
                    $fileObj->getPathname(),
                    $watermarkedPdf,
                    $watermarkPath
                );

                if (!$watermarkSuccess) {
                    throw new \Exception("PDF添加水印失败");
                }

                // 使用加水印后的文件
                $processedFile = $watermarkedPdf;
                $fileObj = new \think\File($watermarkedPdf);
            }

            // === 上传到OSS ===
            $fileName = $this->imageHandler->createFileName($fileExtension);
            $directoryValue = config('filesystem.file.image.directory')[$directory];
            $filePath = $this->imageHandler->createOssFilePath($fileName, $directoryValue);

            $result = $this->aliyun_oss->uploadFile($filePath, $fileObj);

            // 清理临时文件
            if ($processedFile && file_exists($processedFile)) {
                unlink($processedFile);
            }

            if (!$result) {
                throw new \Exception("上传到OSS失败");
            }

            // === 保存到数据库 ===
            $imageModel = new Images();
            $url =  '/' . $filePath;
            $imageModel->url = $url;
            $imageModel->directory = $directory;
            $imageModel->name = $originalName;

            $imageModel->save();

            $return['data'] = [
                'url' => $this->oss_domain_name . '/' . $filePath,
                'id' => $imageModel->id,
                'name' => $imageModel->name,
            ];
            $return['message'] = '上传成功';
        } catch (\Exception $e) {
            $return['code'] = 100044;
            $return['message'] = '上传失败：' . $e->getMessage();

            // 清理可能的临时文件
            if (isset($processedFile) && file_exists($processedFile)) {
                unlink($processedFile);
            }
        }

        return $return;
    }

    /**
     * 使用FPDI添加PDF水印（完全修复版）
     */
    private function addPdfWatermark(string $inputPdf, string $outputPdf, string $watermarkImage): bool
    {
        // ================== 主方法：带回退机制的水印处理 ==================
        try {
            Log::INFO("尝试使用普通方法添加水印");
            // 先尝试普通PDF处理方法
            $result = $this->addPdfWatermarkNormal($inputPdf, $outputPdf, $watermarkImage);
            if ($result) {
                return true;
            }
        } catch (\Exception $e) {
            Log::WARN("普通方法失败: " . $e->getMessage());
        }

        Log::WARN("普通方法失败，尝试压缩PDF专用方法");
        // 普通方法失败时使用压缩PDF处理方法
        return $this->addPdfWatermarkForCompressed($inputPdf, $outputPdf, $watermarkImage);
    }

    // ================== 普通PDF处理方法 ==================
    private function addPdfWatermarkNormal(string $inputPdf, string $outputPdf, string $watermarkImage): bool
    {
        try {
            // 初始化FPDI处理器（支持多个库版本）
            $pdf = null;
            if (class_exists('\setasign\Fpdi\Tcpdf\Fpdi')) {
                $pdf = new \setasign\Fpdi\Tcpdf\Fpdi();
            } elseif (class_exists('\setasign\tcpdi\TCPDI')) {
                $pdf = new \setasign\tcpdi\TCPDI();
            } else {
                $pdf = new \setasign\Fpdi\Fpdi();
            }

            $pdf->SetAutoPageBreak(false);

            // 验证源文件
            $inputPdf = realpath($inputPdf);
            if (!file_exists($inputPdf)) {
                throw new \Exception("源PDF文件不存在: $inputPdf");
            }

            // 获取PDF页数
            $pageCount = $pdf->setSourceFile($inputPdf);

            // 验证水印图片
            if (!file_exists($watermarkImage)) {
                throw new \Exception("水印图片不存在: $watermarkImage");
            }
            $imgInfo = getimagesize($watermarkImage);
            if ($imgInfo === false) {
                throw new \Exception("水印图片格式无效");
            }
            [$wmWidth, $wmHeight] = $imgInfo;

            // 逐页处理
            for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
                $templateId = $pdf->importPage($pageNo);
                $size = $pdf->getTemplateSize($templateId);
                [$pageWidth, $pageHeight] = [$size['width'], $size['height']];
                $orientation = $pageWidth > $pageHeight ? 'L' : 'P';

                $pdf->AddPage($orientation, [$pageWidth, $pageHeight]);
                $pdf->useTemplate($templateId, 0, 0, $pageWidth, $pageHeight, true);

                // 水印尺寸计算（占页面20%-30%）
                $scale = min($pageWidth / $wmWidth, $pageHeight / $wmHeight) * 0.25;
                $scale = min($scale, 0.3); // 限制最大比例
                [$targetWidth, $targetHeight] = [$wmWidth * $scale, $wmHeight * $scale];

                // === 修改开始：三个水印位置 ===
                $verticalPositions = [0.2, 0.5, 0.8]; // 上、中、下位置（页面高度百分比）

                foreach ($verticalPositions as $position) {
                    $y = $pageHeight * $position - $targetHeight / 2;
                    $x = ($pageWidth - $targetWidth) / 2;

                    // 添加半透明旋转水印
                    $pdf->SetAlpha(0.1);
                    $pdf->StartTransform();
                    $pdf->Rotate(45, $x + $targetWidth / 2, $y + $targetHeight / 2);
                    $pdf->Image(
                        $watermarkImage,
                        $x,
                        $y,
                        $targetWidth,
                        $targetHeight
                    );
                    $pdf->StopTransform();
                    $pdf->SetAlpha(1);
                }
                // === 修改结束 ===
            }

            $pdf->Output($outputPdf, 'F');
            return file_exists($outputPdf) && filesize($outputPdf) > 1024;
        } catch (\Exception $e) {
            throw new \Exception("普通方法错误: " . $e->getMessage());
        }
    }

    // ================== 压缩PDF处理方法 ==================
    private function addPdfWatermarkForCompressed(string $inputPdf, string $outputPdf, string $watermarkImage): bool
    {
        try {
            // 使用Smalot解析器处理压缩PDF
            $parser = new \Smalot\PdfParser\Parser();
            $pdf = $parser->parseFile($inputPdf);
            $pages = $pdf->getPages();

            // 初始化TCPDF写入器
            $pdfWriter = new \TCPDF();
            $pdfWriter->SetAutoPageBreak(false);
            $pdfWriter->setPrintHeader(false);
            $pdfWriter->setPrintFooter(false);

            // 加载中文字体
            $fontPath = root_path() . 'vendor/tecnickcom/tcpdf/fonts/stsongstdlight.php';
            if (!file_exists($fontPath)) {
                throw new \Exception("中文字体文件缺失");
            }
            $pdfWriter->AddFont('stsongstdlight', '', $fontPath);
            $pdfWriter->SetFont('stsongstdlight', '', 10);

            // 准备水印图片
            $watermarkData = file_get_contents($watermarkImage);
            $imgInfo = getimagesizefromstring($watermarkData);
            [$wmWidth, $wmHeight] = [$imgInfo[0], $imgInfo[1]];

            // 逐页重建
            foreach ($pages as $pageIndex => $page) {
                $details = $page->getDetails();
                [$width, $height] = isset($details['Page size'])
                    ? array_map(fn($v) => floatval($v) * 0.3528, explode('x', $details['Page size']))
                    : [210, 297]; // 默认A4尺寸

                $pdfWriter->AddPage($width > $height ? 'L' : 'P', [$width, $height]);

                // 优先渲染图像内容
                $imageFound = false;
                foreach ($page->getXObjects() as $xobject) {
                    if ($xobject instanceof \Smalot\PdfParser\XObject\Image) {
                        $tmpImage = tempnam(sys_get_temp_dir(), 'pdfimg');
                        file_put_contents($tmpImage, $xobject->getContent());
                        $pdfWriter->Image($tmpImage, 0, 0, $width, $height);
                        unlink($tmpImage);
                        $imageFound = true;
                        break;
                    }
                }

                // 无图像则渲染文本
                if (!$imageFound && ($content = trim($page->getText()))) {
                    $pdfWriter->writeHTML('<div>' . nl2br(htmlspecialchars($content)) . '</div>');
                }

                // === 修改开始：添加上、中、下三个位置水印 ===
                $scale = min($width / $wmWidth, $height / $wmHeight) * 0.25;
                [$targetWidth, $targetHeight] = [$wmWidth * $scale, $wmHeight * $scale];

                // 定义三个垂直位置（页面上20%、50%、80%高度）
                $verticalPositions = [0.2, 0.5, 0.8];

                foreach ($verticalPositions as $position) {
                    $y = $height * $position - $targetHeight / 2; // 垂直居中定位点
                    $x = ($width - $targetWidth) / 2; // 水平居中

                    $pdfWriter->SetAlpha(0.1);
                    $pdfWriter->StartTransform();
                    $pdfWriter->Rotate(45, $x + $targetWidth / 2, $y + $targetHeight / 2);
                    $pdfWriter->Image('@' . $watermarkData, $x, $y, $targetWidth, $targetHeight);
                    $pdfWriter->StopTransform();
                    $pdfWriter->SetAlpha(1);
                }
                // === 修改结束 ===
            }

            $pdfWriter->Output($outputPdf, 'F');
            return file_exists($outputPdf);
        } catch (\Exception $e) {
            throw new \Exception("压缩方法错误: " . $e->getMessage());
        }
    }

    /**
     * @describe file 文件流上传图片
     * @author: Bobo
     * @date: 2024-08-27 10:15:22
     * @param [type] @required
     * @return Renderable
     */
    public function uploadImages(File $fileObj, int $directory, int $isThumb = 0, int $thumbWidth = 0, $is_mark = 0)
    {
        // 初始化返回结果数组
        $return = ['code' => 0, 'message' => '上传成功', 'data' => []];

        // 获取文件临时路径和扩展名
        // $fileTempPath = $fileObj->getRealPath();
        $originalName = $fileObj instanceof \think\file\UploadedFile ? $fileObj->getOriginalName() : basename($fileObj->getPathname());
        $fileExtension = $fileObj instanceof \think\file\UploadedFile ? $fileObj->getOriginalExtension() : pathinfo($fileObj->getPathname(), PATHINFO_EXTENSION);
        // $file = new File($fileTempPath);


        try {
            // 生成文件名和目录路径
            $fileName = $this->imageHandler->createFileName($fileExtension);
            $directoryValue = config('filesystem.file.image.directory')[$directory];

            $filePath = $this->imageHandler->createOssFilePath($fileName, $directoryValue);

            // 上传原始文件到阿里云OSS
            $result = $this->aliyun_oss->uploadFile($filePath, $fileObj);

            $resultThumb = null; // 初始化 $resultThumb 变量
            if ($isThumb == 1) {
                // 如果需要生成缩略图
                $thumbWidth = $thumbWidth == 0 ? config('filesystem.file.image.thumb_width') : $thumbWidth;
                $thumbFilePath = $this->imageHandler->createOssFilePath($fileName, $directoryValue, 'thumb');
                $thumbResult = $this->imageHandler->generateThumb($fileObj, $thumbWidth);

                if ($thumbResult['code'] == 0) {
                    // 上传缩略图到阿里云OSS
                    $fileThumb = new File($thumbResult['file']->getRealPath());
                    $resultThumb = $this->aliyun_oss->uploadFile($thumbFilePath, $fileThumb);

                    if (!$resultThumb) {
                        $return['message'] = '上传缩略图失败';
                        $return['code'] = 100044;
                        return $return;
                    }
                } else {
                    $return['message'] = '生成缩略图失败' . $thumbResult['message'];
                    $return['code'] = 100044;
                }
            }

            // 删除临时文件
            // unlink($fileTempPath);

            if (1 === $result || ($isThumb === 1 && !$resultThumb)) {
                $return['message'] = '上传失败';
                $return['code'] = 100044;
                return $return;
            }

            // 保存图片信息到数据库
            $imageModel = new Images();
            $url = '/' . $filePath;
            $imageModel->url = $url;
            $imageModel->name = $originalName;

            if ($isThumb == 1 && $resultThumb) {
                $imageModel->thumb = '/' . $thumbFilePath;
                $localRealThumbName = $thumbResult['file']->getfileName();
                unlink($this->image['temp_path'] . $localRealThumbName);
            }

            $imageModel->directory = $directory;
            $imageModel->save();

            // 返回上传成功的数据
            $return['data'] = [
                'id' => $imageModel->id,
                'url' => $this->oss_domain_name  . $imageModel->url,
                'name' => $imageModel->name,
            ];

            if ($isThumb == 1 && $resultThumb) {
                $return['data']['thumb'] = $this->oss_domain_name . $imageModel->thumb;
            }
        } catch (\Exception $e) {
            // 捕获异常并返回错误信息
            $return['code'] = 100044;
            $return['message'] = '上传失败：' . $e->getMessage();
        }

        // 返回结果数组
        return $return;
    }

    /**
     * @describe 获取OSS直传参数
     * @author: Bobo
     * @date: 2025-01-03 
     * @param int $directory 目录类型
     * @param string $fileExtension 文件扩展名
     * @param int $maxSize 最大文件大小(MB)，默认20MB
     * @return array
     */
    public function getOssDirectUploadParams(int $directory, string $fileExtension = '', int $maxSize = 1024)
    {
        $return = [
            'code' => 0,
            'message' => '',
            'data' => []
        ];

        try {
            $env = env('app_debug');
            if ($env == 'dev') {
                $directory = 0;
            }
            $directoryValue = config('filesystem.file.image.directory')[$directory];


            // 获取OSS配置
            $accessKeySecret = config('filesystem.aliyun-oss.access_key_secret');
            $bucket = config('filesystem.aliyun-oss.bucket');
            $endpoint = config('filesystem.aliyun-oss.endpoint');

            // 设置上传策略
            $expire = time() + 3600; // 1小时后过期
            $maxFileSize = $maxSize * 1024 * 1024; // 转换为字节

            $policy = [
                'expiration' => date('Y-m-d\TH:i:s\Z', $expire), // 过期时间
                'conditions' => [
                    ['bucket' => $bucket],
                    ['starts-with', '$key', $directoryValue . '/'], // 限制上传路径
                    ['content-length-range', 0, $maxFileSize], // 限制文件大小
                ]
            ];

            // 如果指定了文件扩展名，添加文件类型限制
            if ($fileExtension) {
                $mimeType = $this->getMimeType($fileExtension);
                if ($mimeType) {
                    $policy['conditions'][] = ['starts-with', '$Content-Type', $mimeType];
                }
            }

            $policyBase64 = base64_encode(json_encode($policy));
            $signature = base64_encode(hash_hmac('sha1', $policyBase64, $accessKeySecret, true));

            // 构建上传URL
            $host = "https://{$bucket}.{$endpoint}";

            $return['data'] = [
                'host' => $host,
                'policy' => $policyBase64,
                'signature' => $signature,
                // 'access_key_id' => $accessKeyId,
                // 'key' => $filePath, // 建议的文件路径
                'expire' => $expire,
                'directory' => $directoryValue,
                'max_file_size' => $maxFileSize,
                'success_action_status' => '200', // 上传成功后返回的状态码
            ];

            $return['message'] = '获取直传参数成功';
        } catch (\Exception $e) {
            $return['code'] = 100045;
            $return['message'] = '获取直传参数失败：' . $e->getMessage();
        }

        return $return;
    }

    /**
     * @describe 根据文件扩展名获取MIME类型
     * @param string $extension
     * @return string
     */
    private function getMimeType(string $extension): string
    {
        $mimeTypes = [
            'jpg' => 'image/jpeg',
            'jpeg' => 'image/jpeg',
            'png' => 'image/png',
            'gif' => 'image/gif',
            'webp' => 'image/webp',
            'pdf' => 'application/pdf',
            'doc' => 'application/msword',
            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'xls' => 'application/vnd.ms-excel',
            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'txt' => 'text/plain',
            'zip' => 'application/zip',
            'mp4' => 'video/mp4',
            'mp3' => 'audio/mpeg',
        ];

        return $mimeTypes[strtolower($extension)] ?? '';
    }


    /**
     * @describe 直传成功后保存文件信息到数据库
     * @author: Bobo
     * @date: 2025-01-03
     * @param string $filePath OSS文件路径
     * @param int $directory 目录类型  
     * @param string $originalName 原始文件名
     * @return array
     */
    public function saveDirectUploadInfo(string $filePath, int $directory, string $originalName = '')
    {
        $return = [
            'code' => 0,
            'message' => '',
            'data' => []
        ];

        try {
            // 保存到数据库
            $imageModel = new Images();
            $url = '/' . $filePath;
            $imageModel->url = $url;
            $imageModel->directory = $directory;
            $imageModel->name = $originalName ?: basename($filePath);
            $imageModel->save();

            $return['data'] = [
                'url' => $this->oss_domain_name . '/' . $filePath,
                'id' => $imageModel->id,
                'name' => $imageModel->name,
            ];
            $return['message'] = '保存成功';
        } catch (\Exception $e) {
            $return['code'] = 100046;
            $return['message'] = '保存失败：' . $e->getMessage();
        }

        return $return;
    }

    /**
     * @describe 批量保存OSS图片至数据库
     * @author: Bobo
     * @date: 2025-07-21 16:12:37 
     * @param [type] @required
     * @return Renderable
     */
    public function batchSaveDirectUploadInfo(array $images)
    {
        $return = [
            'code' => 0,
            'message' => '',
            'data' => []
        ];
        try {
            // 保存到数据库
            $imageModel = new Images();
            $dataList = [];
            $env = env('app_debug');
            $imageModel->startTrans();
            foreach ($images as $image) {
                $data = [
                    'url' => '/' . $image['url'],
                    'directory' => $env == 'dev' ? 0 : $image['directory'], // 开发环境目录为0
                    'name' => $image['name'],
                    'create_time' => date('Y-m-d H:i:s'),
                ];
                $id = $imageModel->insertGetId($data);
                $data['id'] = $id;
                $dataList[] = $data;
            }
            $return['data'] = $dataList;
            $imageModel->commit();
        } catch (\Exception $e) {
            $imageModel->rollback();
            $return['code'] = 100046;
            $return['message'] = '保存失败：' . $e->getMessage();
        }

        return $return;
    }
}
